package com.cx.auth.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.exception.ServiceException;


import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 以下的断言方法是为了减少if/else。方便即使返回异常结果。
 */
public class Assert {

    public static void isTrue(boolean expression, String errorMsgTemplate, Object... params) throws ServiceException {
        if (false == expression) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
    }

    public static void isFalse(boolean expression, String errorMsgTemplate, Object... params) throws ServiceException {
        if (expression) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
    }

    public static void isNull(Object object, String errorMsgTemplate, Object... params) throws ServiceException {
        if (object != null) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
    }

    public static <T> T notNull(T object, String errorMsgTemplate, Object... params) throws ServiceException {
        if (object == null) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return object;
    }

    public static String notEmpty(String text, String errorMsgTemplate, Object... params) throws ServiceException {
        if (StrUtil.isEmpty(text)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return text;
    }

    public static String notBlank(String text, String errorMsgTemplate, Object... params) throws ServiceException {
        if (StrUtil.isBlank(text)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return text;
    }

    public static String notBlank(String text) throws ServiceException {
        return notBlank(text, "[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
    }

    public static String notContain(String textToSearch, String substring, String errorMsgTemplate, Object... params) throws ServiceException {
        if (StrUtil.isNotEmpty(textToSearch) && StrUtil.isNotEmpty(substring) && textToSearch.contains(substring)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return substring;
    }

    public static String notContain(String textToSearch, String substring) throws ServiceException {
        return notContain(textToSearch, substring, "[Assertion failed] - this String argument must not contain the substring [{}]", substring);
    }

    public static Object[] notEmpty(Object[] array, String errorMsgTemplate, Object... params) throws ServiceException {
        if (ArrayUtil.isEmpty(array)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return array;
    }

    public static Object[] notEmpty(Object[] array) throws ServiceException {
        return notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
    }

    public static <T> T[] noNullElements(T[] array, String errorMsgTemplate, Object... params) throws ServiceException {
        if (ArrayUtil.hasNull(array)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return array;
    }

    public static <T> T[] noNullElements(T[] array) throws ServiceException {
        return noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
    }

    public static <T> Collection<T> notEmpty(Collection<T> collection, String errorMsgTemplate, Object... params) throws ServiceException {
        if (CollectionUtil.isEmpty(collection)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return collection;
    }

    public static <T> Collection<T> notEmpty(Collection<T> collection) throws ServiceException {
        return notEmpty(collection, "[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
    }

    public static <K, V> Map<K, V> notEmpty(Map<K, V> map, String errorMsgTemplate, Object... params) throws ServiceException {
        if (CollectionUtil.isEmpty(map)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return map;
    }

    public static <K, V> Map<K, V> notEmpty(Map<K, V> map) throws ServiceException {
        return notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry");
    }

    public static <T> T isInstanceOf(Class<?> type, T obj) {
        return isInstanceOf(type, obj, "Object [{}] is not instanceof [{}]", obj, type);
    }

    public static <T> T isInstanceOf(Class<?> type, T obj, String errorMsgTemplate, Object... params) throws ServiceException {
        notNull(type, "Type to check against must not be null");
        if (false == type.isInstance(obj)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
        return obj;
    }

    public static void isAssignable(Class<?> superType, Class<?> subType) throws ServiceException {
        isAssignable(superType, subType, "{} is not assignable to {})", subType, superType);
    }

    public static void isAssignable(Class<?> superType, Class<?> subType, String errorMsgTemplate, Object... params) throws ServiceException {
        notNull(superType, "Type to check against must not be null");
        if (subType == null || !superType.isAssignableFrom(subType)) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
    }

    public static void state(boolean expression, String errorMsgTemplate, Object... params) throws ServiceException {
        if (false == expression) {
            throw new ServiceException(StrUtil.format(errorMsgTemplate, params));
        }
    }

    /**
     * 检查下标（数组、集合、字符串）是否符合要求，下标必须满足：
     *
     * <pre>
     * 0 <= index < size
     * </pre>
     *
     * @param index 下标
     * @param size 长度
     * @return 检查后的下标
     * @throws ServiceException 如果size < 0 抛出此异常
     * @throws IndexOutOfBoundsException 如果index < 0或者 index >= size 抛出此异常
     * @since 4.1.9
     */
    public static int checkIndex(int index, int size) throws ServiceException, IndexOutOfBoundsException {
        return checkIndex(index, size, "[Assertion failed]");
    }

    /**
     * 检查下标（数组、集合、字符串）是否符合要求，下标必须满足：
     *
     * <pre>
     * 0 <= index < size
     * </pre>
     *
     * @param index 下标
     * @param size 长度
     * @param errorMsgTemplate 异常时的消息模板
     * @param params 参数列表
     * @return 检查后的下标
     * @throws ServiceException 如果size < 0 抛出此异常
     * @throws IndexOutOfBoundsException 如果index < 0或者 index >= size 抛出此异常
     * @since 4.1.9
     */
    public static int checkIndex(int index, int size, String errorMsgTemplate, Object... params) throws ServiceException, IndexOutOfBoundsException {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException(badIndexMsg(index, size, errorMsgTemplate, params));
        }
        return index;
    }

    /**
     * 检查值是否在指定范围内
     *
     * @param value 值
     * @param min 最小值（包含）
     * @param max 最大值（包含）
     * @return 检查后的长度值
     * @since 4.1.10
     */
    public static int checkBetween(int value, int min, int max) {
        if (value < min || value > max) {
            throw new ServiceException(StrUtil.format("Length must be between {} and {}.", min, max));
        }
        return value;
    }

    /**
     * 检查值是否在指定范围内
     *
     * @param value 值
     * @param min 最小值（包含）
     * @param max 最大值（包含）
     * @return 检查后的长度值
     * @since 4.1.10
     */
    public static long checkBetween(long value, long min, long max) {
        if (value < min || value > max) {
            throw new ServiceException(StrUtil.format("Length must be between {} and {}.", min, max));
        }
        return value;
    }

    /**
     * 检查值是否在指定范围内
     *
     * @param value 值
     * @param min 最小值（包含）
     * @param max 最大值（包含）
     * @return 检查后的长度值
     * @since 4.1.10
     */
    public static double checkBetween(double value, double min, double max) {
        if (value < min || value > max) {
            throw new ServiceException(StrUtil.format("Length must be between {} and {}.", min, max));
        }
        return value;
    }

    /**
     * 检查值是否在指定范围内
     *
     * @param value 值
     * @param min 最小值（包含）
     * @param max 最大值（包含）
     * @return 检查后的长度值
     * @since 4.1.10
     */
    public static Number checkBetween(Number value, Number min, Number max) {
        notNull(value,"");
        notNull(min,"");
        notNull(max,"");
        double valueDouble = value.doubleValue();
        double minDouble = min.doubleValue();
        double maxDouble = max.doubleValue();
        if (valueDouble < minDouble || valueDouble > maxDouble) {
            throw new ServiceException(StrUtil.format("Length must be between {} and {}.", min, max));
        }
        return value;
    }

    // -------------------------------------------------------------------------------------------------------------------------------------------- Private method start
    /**
     * 错误的下标时显示的消息
     *
     * @param index 下标
     * @param size 长度
     * @param desc 异常时的消息模板
     * @param params 参数列表
     * @return 消息
     */
    private static String badIndexMsg(int index, int size, String desc, Object... params) {
        if (index < 0) {
            return StrUtil.format("{} ({}) must not be negative", StrUtil.format(desc, params), index);
        } else if (size < 0) {
            throw new ServiceException("negative size: " + size);
        } else { // index >= size
            return StrUtil.format("{} ({}) must be less than size ({})", StrUtil.format(desc, params), index, size);
        }
    }

    /**
     * long型变量不能是0
     * @param orgId
     * @param errorMessage
     */
    public static void notZero(long orgId, String errorMessage) {
        if(orgId == 0L){
            throw new ServiceException(errorMessage);
        }
    }

    public static <T> void isEmpty(String str, String errorMessage, Object... params) {
        if(StrUtil.isNotEmpty(str)){
            throw new ServiceException(StrUtil.format(errorMessage, params));
        }
    }

    /**
     * 判断集合是否为空
     * @param collection
     * @param errorMessage
     * @param params
     * @param <T>
     */
    public static <T> void isEmpty(List<T> collection, String errorMessage, Object... params) {
        if(CollUtil.isNotEmpty(collection)){
            throw new ServiceException(StrUtil.format(errorMessage, params));
        }
    }

    /**
     * 判断map是否为空
     * @param map
     * @param errorMessage
     * @param params
     * @param <K>
     * @param <V>
     */
    public static <K, V> void isEmpty(Map<K, V> map, String errorMessage, Object... params) {
        if(CollUtil.isNotEmpty(map)){
            throw new ServiceException(StrUtil.format(errorMessage, params));
        }
    }
}
